﻿using System.Linq;
using System.Windows.Forms;

namespace Borg.Framework
{
    public class TabControl : System.Windows.Forms.TabControl
    {
        private SlotInfo _slot;

        public TabControl()
        {
            Enabled = false;
        }

        public void Attach(SlotInfo slot)
        {
            _slot = slot;
            Enabled = true;
            Register();
        }

        public void Deregistered(object sender, RegisterEventArgs args)
        {
            foreach (var page in TabPages.Cast<TabPage>().
                Where(page => page.PlugTypeInfo == args.PlugTypeInfo))
            {
                TabPages.Remove(page);
                break;
            }
        }

        public void Detach()
        {
            if (_slot != null)
            {
                Unregister();
                _slot = null;
            }

            Enabled = false;
        }

        protected override void OnSelected(TabControlEventArgs e)
        {
            if (!Runtime.IsCompositionAllowed)
                return;

            base.OnSelected(e);

            if (e.TabPage == null)
                return;

            var tabPage = e.TabPage as TabPage;

            if (tabPage == null || _slot.ExtensionInfo.IsReleased)
                return;

            if (tabPage.PlugInfo == null)
            {
                tabPage.PlugInfo = tabPage.PlugTypeInfo.CreateExtension(_slot);
                _slot.Plug(tabPage.PlugInfo);
            }

            _slot.SelectedPlug = tabPage.PlugInfo;
        }

        public void Plugged(object sender, PlugEventArgs args)
        {
            foreach (TabPage page in TabPages)
            {
                if (page.PlugTypeInfo != args.PlugInfo.PlugTypeInfo)
                    continue;

                var extension = (ITabPage)args.Extension;
                
                page.PlugInfo = args.PlugInfo;
                page.Controls.Add(extension.Control);

                if (PageAdded != null)
                    PageAdded(extension);

                break;
            }
        }

        private void Register()
        {
            _slot.Registered += Registered;
            _slot.Deregistered += Deregistered;

            _slot.Plugged += Plugged;
            _slot.Unplugged += Unplugged;

            _slot.SelectionChanged += SelectionChanged;

            if (Runtime.IsCompositionAllowed)
                _slot.SelectionMode = SelectionMode.Single;

            Runtime.TaskQueue.InvokeOnEmptied(SelectFirstTab);
        }

        public void Registered(object sender, RegisterEventArgs args)
        {
            var caption = GetCaption != null
                ? GetCaption(args.PlugTypeInfo)
                : args.PlugTypeInfo.ToString();

            var order = GetOrder != null
                ? GetOrder(args.PlugTypeInfo)
                : 0f;

            var tabPage = new TabPage(args.PlugTypeInfo, caption, order);
            var i = 0;

            while (i < TabPages.Count)
            {
                if (tabPage.Order < ((TabPage) TabPages[i]).Order)
                    break;

                i++;
            }

            TabPages.Insert(i, tabPage);

            if (_slot.PluggedPlugInfos.Count == 0)
                Runtime.TaskQueue.InvokeOnEmptied(SelectFirstTab);
        }

        public void SelectFirstTab()
        {
            if (TabPages.Count != 0 && _slot.SelectedPlugs.Count == 0 && !_slot.ExtensionInfo.IsReleased)
                OnSelected(new TabControlEventArgs(TabPages[0], 0, TabControlAction.Selected));
        }

        private void SelectionChanged(object sender, SelectionChangedEventArgs args)
        {
            foreach (var plug in args.AddedPlugs)
            {
                foreach (TabPage page in TabPages)
                {
                    if (page.PlugTypeInfo != plug.PlugTypeInfo)
                        continue;

                    SelectTab(page);
                    break;
                }
            }
        }

        private void Unplugged(object sender, PlugEventArgs args)
        {
            foreach (TabPage page in TabPages)
            {
                if (page.PlugTypeInfo != args.PlugInfo.PlugTypeInfo)
                    continue;

                page.Controls.Clear();
                page.PlugInfo = null;
                break;
            }
        }

        private void Unregister()
        {
            _slot.Registered -= Registered;
            _slot.Deregistered -= Deregistered;

            _slot.Plugged -= Plugged;
            _slot.Unplugged -= Unplugged;

            _slot.SelectionChanged -= SelectionChanged;
        }

        public GetCaptionDelegate GetCaption { get; set; }

        public GetOrderDelegate GetOrder { get; set; }

        public PageAddedDelegate PageAdded { get; set; }

        public delegate string GetCaptionDelegate(PlugTypeInfo pti);

        public delegate float GetOrderDelegate(PlugTypeInfo pti);

        public delegate void PageAddedDelegate(ITabPage page);
    }
}

